Handling Events
OpenDoc calls a part'sHandleEvent
method when a user event occurs within the purview of a focus currently owned by the part. For example, keystroke events are dispatched to the part that owns the keystroke focus. Geometry-based events, such as mouse clicks, are generally dispatched to the part within whose frames they occur, regardless of which part is currently active.If the part editor handles the event, it should return a value of
kODTrue
. It can return a value ofkODFalse
if it does not handle the event. If the frame'sDoesPropagateEvents
method returnskODTrue
, then the event is sent to the containing frame. If all containing frames fail to handle the event, and they propagate it, the OpenDoc shell attempts to handle the event itself.For a given event, the dispatcher locates a dispatch module, and the dispatch module calls the part's
HandleEvent
method. Thefacet
parameter of theHandleEvent
method may be null (kODNULL
), depending on the kind of event. Theframe
parameter is always valid, except in the case of some null (idle) events.Event Constants
OpenDoc expects parts to handle the user events that are standard on the Mac OS, as represented by the following list of constants:
kODEvtNull kODEvtMouseDown kODEvtMouseUp kODEvtKeyDown kODEvtKeyUp kODEvtAutoKey kODEvtUpdate kODEvtActivate kODEvtOSIn addition to the standard Mac OS user events, parts should expect to receive OpenDoc-defined events. All parts may receive the events represented by the following constants:
kODEvtMenu kODEvtWindow kODEvtMouseEnter kODEvtMouseWithin kODEvtMouseLeave kODEvtBGMouseDownContainer parts (those that can embed other parts) may also receive the events represented by the following constants:
kODEvtMouseDownEmbedded kODEvtMouseUpEmbedded kODEvtMouseDownBorder kODEvtMouseUpBorder kODEvtBGMouseDownEmbeddedThe constant names representing the events differ slightly from the standard Mac OS event names for cross-platform compatibility. Part editors handle these events differently according to their own requirements. Refer to the OpenDoc Programmer's Guide for the Mac OS for detailed information about handling these types of events.The HandleEvent Method
Generally, the implementation of a part editor'sHandleEvent
method works in much the same way as event-handling code in a standard Mac OS application. That is, the implementation acquires the event record, then branches to the appropriate event-handling routine based on the type of event. Unlike the standard Mac OS application, you don't need to poll for events by callingWaitNextEvent
; in the case of standard events, the event record is passed as a parameter to theHandleEvent
method.The
SamplePart
object's implementation of theHandleEvent
method performs the following actions:
Listing 2-27 shows the implementation of the
- Performs a case switch on expected events.
An event is represented by an OpenDoc constant compared to thewhat
field of anODEventData
structure, a pointer to which is passed in theevent
parameter of theHandleEvent
call.
- Branches to the appropriate subroutine method.
TheHandleEvent
method handles simple events without branching.
- Returns a Boolean value indicating whether or not the event was handled.
HandleEvent
method.Listing 2-27
HandleEvent
method
ODBoolean SamplePart::HandleEvent( Environment* ev, ODEventData* event, ODFrame* frame, ODFacet* facet, ODEventInfo* eventInfo ) { SOM_Trace("SamplePart","HandleEvent"); ODBooleaneventHandled = kODFalse; switch ( event->what ) { case kODEvtMouseDown: case kODEvtMouseUp: eventHandled = this->HandleMouseEvent(ev, event, facet, eventInfo); break; case kODEvtMenu: eventHandled = this->HandleMenuEvent(ev, event, frame); break; case kODEvtActivate: this->WindowActivating(ev, frame, (event->modifiers & activeFlag)); eventHandled = kODTrue; break; case kODEvtMouseEnter: case kODEvtMouseLeave: SetCursor(&ODQDGlobals.arrow); eventHandled = kODTrue; break; case kODEvtMouseWithin: eventHandled = kODTrue; break; case kODEvtNull: case kODEvtMouseDownEmbedded: case kODEvtMouseUpEmbedded: case kODEvtMouseDownBorder: case kODEvtMouseUpBorder: case kODEvtWindow: case kODEvtKeyDown: case kODEvtKeyUp: case kODEvtAutoKey: case kODEvtOS: case kODEvtDisk: default: break; } return eventHandled; }TheSamplePart
object'sHandleEvent
method illustrates a minimal set of event handlers that every part editor should implement. Naturally, you must also prepare to handle other events to which your part must respond to behave correctly.The HandleMouseEvent Method
SamplePart calls its own internalHandleMouseEvent
method from itsHandleEvent
method when it receives a mouse event of typekODEvtMouseUp
orkODEvtMouseDown
. OpenDoc passes the mouse event as a parameter to the part'sHandleEvent
method when the user clicks the mouse button within the bounds of one of the part's facets.When a frame is inactive, the first mouse-up event (
kODEvtMouseUp
) it receives should activate it. Inactive frames do not receive mouse-down events (kODEvtMouseDown
).The
HandleMouseEvent
method performs the following actions:
Listing 2-28 shows the
- Ensures that the facet in which the mouse event occurred is valid.
If thefacet
parameter is null, the mouse event occurred outside the bounds of a modal window, in which case the implementation causes the Mac OS to sound a single system beep.
- Handles a mouse-up event.
After determining that the event occurred inside a valid facet, the method tests the event type against thekODEvtMouseUp
constant.
- Handles the window's activation state.
If the event is a mouse-up event,HandleMouseEvent
checks the facet's window. If the window is not active, the method selects it and returns a value ofkODTrue
, which indicates that the method handled the mouse-up event. If the facet's window is already active, the method continues.
- Handles the frame's activation state.
HandleMouseEvent
retrieves the facet's frame and the frame'sCFrameInfo
part info object. Using this information, the method determines if this is the active frame; if not, it calls itsActivateFrame
method, which activates the frame by requesting the selection and menu foci.The method stores the active facet in its frame's
CFrameInfo
object, so the part editor will be able to position a part window properly if the user later chooses the View as Window command. If theActivateFrame
method call returned successfully,HandleMouseEvent
returnskODTrue
; otherwise it returnskODFalse
.
- Handles a mouse-down event.
If the event was not a mouse-up event,HandleMouseEvent
tests if it was of typekODEvtMouseDown
. If so, the method localizes the coordinates of the mouse-down event to the facet's coordinates and calls theSamplePart
object's internalDoMouseEvent
method.The
SamplePart
object'sDoMouseEvent
method is empty. A part editor with real work to do in response to a mouse-down event would do it at this point. For example, if your part supports selection of its content by dragging the mouse, as with a marquee or lasso tool, you would handle those events at this point. Similarly, you would handle buttons or other controls here if they were managed directly by your part.
HandleMouseEvent
method. TheActivateFrame
method is included in the "Activation" section as Listing 2-38.Listing 2-28
HandleMouseEvent
method
ODBoolean SamplePart::HandleMouseEvent( Environment* ev, ODEventData* event, ODFacet* facet, ODEventInfo* eventInfo ) { SOM_Trace("SamplePart","HandleMouseEvent"); if ( facet != kODNULL ) { if ( event->what == kODEvtMouseUp ) { ODWindow* window = facet->GetWindow(ev); TRY if ( !window->IsActive(ev) ) window->Select(ev); else { ODFrame* frame = facet->GetFrame(ev); CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); if ( !frameInfo->IsFrameActive() ) { if ( this->ActivateFrame(ev, frame) ) frameInfo->SetActiveFacet(facet); else return kODFalse; } } CATCH_ALL ENDTRY } else if ( event->what == kODEvtMouseDown ) { Point where; where.h = FixedToInt(eventInfo->where.x); where.v = FixedToInt(eventInfo->where.y); this->DoMouseEvent(ev, facet, &where); } } else { SysBeep(1); } return kODTrue; }The HandleMenuEvent Method
SamplePart calls its own internalHandleMenuEvent
method when it receives a menu event (typekODEvtMenu
). OpenDoc converts a mouse-down event that occurs in the menu bar, or its keyboard equivalent, into a menu event. On receiving an event of this type, theSamplePart
object'sHandleEvent
method callsHandleMenuEvent
, passing the event record and a pointer to the active frame.The
HandleMenuEvent
method performs the following actions:
Listing 2-29 shows the implementation of the
- Retrieves the message field of the event record.
The method uses the message field to determine the number of the menu (contained in the high-order word) and the number of the menu item (contained in the low-order word), for the menu selection made by the user.
- Retrieves the position-independent number of the command.
With the menu and item numbers, the method calls the menu bar object'sGetCommand
method, which returns the command number of the user's menu selection.
- Branches to the appropriate command handler method.
Comparing the command number to constants representing the commands SamplePart can handle, theHandleMenuEvent
method proceeds into a switch statement. SamplePart implements only two commands: About and View As Window. These cases call their appropriate subroutine method and returnkODTrue
. The remaining unimplemented command numbers returnkODFalse
by way of the default clause.
HandleMenuEvent
method.Listing 2-29
HandleMenuEvent
method
ODBoolean SamplePart::HandleMenuEvent( Environment* ev, ODEventData* event, ODFrame* frame ) { SOM_Trace("SamplePart","HandleMenuEvent"); ODULong menuResult= event->message; ODUShort menu = HiWord(menuResult); ODUShort item = LoWord(menuResult); switch ( gGlobals->fMenuBar->GetCommand(ev, menu, item) ) { case kODCommandAbout: this->DoDialogBox(ev, frame, kAboutBoxID); break; case kODCommandViewAsWin: this->Open(ev, frame); break; case kODCommandOpen: case kODCommandInsert: case kODCommandPageSetup: case kODCommandPrint: case kODCommandUndo: case kODCommandRedo: case kODCommandCut: case kODCommandCopy: case kODCommandPaste: case kODCommandPasteAs: case kODCommandClear: case kODCommandSelectAll: case kODCommandGetPartInfo: case kODCommandPreferences: default: return kODFalse; } return kODTrue; }The AdjustMenus Method
OpenDoc calls a part'sAdjustMenus
method when a user event of typekODEvtMouseDown
occurs in the menu bar and the same part owns the menu focus.AdjustMenus
is a general-purpose menu-handling method. Its purpose is to ensure that the visible state of the part's menus accurately reflect the state of the part. Accordingly, theAdjustMenus
method enables and disables menu items, depending on whether or not their commands are available, and it changes the menu item text as necessary to describe accurately the actions ensuing from choosing those items.The
SamplePart
object's implementation of theAdjustMenus
method performs the following actions:
Listing 2-30 shows the implementation of the
- Validates the menu bar if this part is the root part.
The menu bar object always calls the root part'sAdjustMenus
method before calling the menu focus owner'sAdjustMenus
method. Any other part can swap out the base menu bar at any time. Therefore, if the menu bar object has changed since it was previously copied, the method recopies the base menu bar from the window-state object. After copying the menu bar, you must also reinstall your part's menus.
- Enables or disables the menu commands, depending on conditions.
The method enables the View As Window command, but only if the frame that owns the menu focus (a pointer to which is passed into the method as it is called) is not the root frame of the window. (The frame that owns the menu focus is usually the active frame.)
- Sets the text of the About menu item correctly.
The method puts a reference to the focus owner's frame into a temporary frame object and tests it against the frame reference passed into this method call. If this frame owns the menu focus, the method gets the About menu item text from the SamplePart menu string resource, creates a temporary international text structure for the text, and sets the menu item. The temporary object automatically disposes of the memory allocated for the international text.
AdjustMenus
method.Listing 2-30
AdjustMenus
method
void SamplePart::AdjustMenus( Environment* ev, ODFrame* frame ) { SOM_Trace("SamplePart","AdjustMenus"); if ( frame->IsRoot(ev) ) { if ( gGlobals->fMenuBar->IsValid(ev) == kODFalse ) { ODReleaseObject(ev, gGlobals->fMenuBar); gGlobals->fMenuBar = ODGetSession(ev,fSelf)->GetWindowState(ev)->CopyBaseMenuBar(ev); } } gGlobals->fMenuBar->EnableCommand(ev, kODCommandViewAsWin, !frame->IsRoot(ev)); TRY ODArbitrator* arbitrator = ODGetSession(ev,fSelf)->GetArbitrator(ev); TempODFrame menuOwner = arbitrator->AcquireFocusOwner(ev, gGlobals->fMenuFocus); if ( ODObjectsAreEqual(ev, frame, menuOwner) ) { Str63 text; ODGetIndString(text, kMenuStringResID, kAboutTextID); TempODIText menuItem(CreateIText(gGlobals->fEditorsScript, gGlobals->fEditorsLanguage, (StringPtr)&text)); gGlobals->fMenuBar->SetItemString(ev, kODCommandAbout, menuItem); } CATCH_ALL // Consume exception ENDTRY }The DoDialogBox Method
SamplePart calls its own internalDoDialogBox
method from itsHandleMenuEvent
method when the user chooses the About command. SamplePart also callsDoDialogBox
from other methods to display error messages to the user. The method illustrates how parts can display a modal dialog box properly.The
DoDialogBox
method performs the following actions:
Listing 2-31 shows the implementation of the
- Gets access to the session object.
Access to the session object is provided by theODGetSession
utility function. The session object, in turn, provides needed access to the arbitrator and window-state objects.
- Gets a valid frame.
Only frames own foci. If the calling method does not pass in a valid frame reference, theDoDialogBox
method gets one from SamplePart's internal list of display frames. This frame requests the modal focus needed to keep other parts from displaying a modal dialog box simultaneously.
- Requests the modal focus from the arbitrator.
If its focus request is not satisfied, the method causes the Mac OS to sound its system beep. Being unable to acquire the modal focus indicates that another modal dialog box is already being displayed.
- Deactivates the frontmost document window.
If its focus request is satisfied, the method calls the window-state object'sDeactivateFrontWindows
method.
- Displays the About box.
The method uses the OpenDoc utility routineBeginUsingLibraryResources
to make the resources in its shared library available and uses the Mac OS Toolbox routineGetNewDialog
to retrieve the dialog resource.If an error number greater than 0 was passed into this method, it sets up an error dialog box to display.
If the dialog box resource has loaded properly, the
DoDialogBox
method ensures that the cursor is an arrow, shows the dialog box window, and calls the Mac OS Toolbox routineModalDialog
to display and handle the user's interaction with the dialog box.
- Cleans up after itself.
The method disposes of the dialog resource returned from the previousGetNewDialog
routine. Finally, it restores the resource chain by callingEndUsingLibraryResources
, relinquishes the modal focus to the arbitrator, and reactivates the frontmost document window.
DoDialogBox
method.Listing 2-31
DoDialogBox
method
void SamplePart::DoDialogBox( Environment* ev, ODFrame* frame, ODSShort dialogID, ODUShort errorNumber ) { SOM_Trace("SamplePart","DoDialogBox"); ODFrame* focusFrame = frame; ODSession*session = ODGetSession(ev,fSelf); if ( focusFrame == kODNULL ) { CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { if ( proxy->FrameIsLoaded() ) focusFrame = proxy->GetFrame(ev); if ( focusFrame ) break; } } if ( session->GetArbitrator(ev)->RequestFocus(ev, gGlobals->fModalFocus, focusFrame) ) { DialogPtrdialog; ODSShort itemHit; session->GetWindowState(ev)->DeactivateFrontWindows(ev); ODSLong rfRef; rfRef = BeginUsingLibraryResources(); { dialog = GetNewDialog(dialogID, kODNULL, (WindowPtr) -1L); if ( dialog ) { if ( errorNumber > 0 ) { HandleitemHandle; Rect itemRect; short itemType; Str255errStr; GetIndString(errStr, kErrorStringResID, errorNumber); GetDialogItem(dialog, kErrStrFieldID, &itemType, &itemHandle, &itemRect); SetDialogItemText(itemHandle, errStr); HideDialogItem(dialog, cancel); SetDialogDefaultItem(dialog, ok); } SetCursor(&ODQDGlobals.arrow); ShowWindow(dialog); ModalDialog(kODNULL, &itemHit); DisposeDialog(dialog); } else { SysBeep(2); } } EndUsingLibraryResources(rfRef); session->GetArbitrator(ev)->RelinquishFocus(ev, gGlobals->fModalFocus, focusFrame); session->GetWindowState(ev)->ActivateFrontWindows(ev); } else SysBeep(2); }The View As Window Command
If the user chooses the View As Window command, theHandleMenuEvent
method calls theSamplePart
object'sOpen
method, which is described in "Opening the Part Into a Window".
Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help